#include "common.h"

ssize_t readn(int fd,void *vptr,size_t n) {
	size_t nleft;
	ssize_t nread;
	char *ptr;

	ptr = (char *)vptr;
	nleft = n;
	while(nleft > 0) {
		if((nread = read(fd,ptr,nleft)) < 0) {
			if(errno == EINTR)
				nread = 0;
			else
				return -1;
		}
		else if(nread == 0)
			break;

		nleft -= nread;
		ptr += nread;
	}

	return n-nleft;

}

ssize_t writen(int fd,const void *vptr,size_t n) {

	size_t nleft;
	ssize_t nwritten;
	const char *ptr;

	ptr = (const char *)vptr;
	nleft = n;
	while(nleft > 0) {
		if((nwritten = write(fd,ptr,nleft)) <= 0) {
			if(nwritten < 0 && errno == EINTR)
				nwritten = 0;
			else
				return -1;
		}

		nleft -= nwritten;
		ptr += nwritten;
	}

	return n;
}

ssize_t readline(int filedes,void *buf,size_t maxlen) {
	ssize_t n,rc;
	char c,*ptr;

	ptr = (char *)buf;
	for(n = 1; n < (ssize_t)maxlen; n++) {
	again:
		if((rc = read(filedes,&c,1)) == 1) {
			*ptr++ = c;
			if(c == '\n')
				break;
		}
		else if(rc == 0) {
			*ptr = 0;
			return (n-1);
		}
		else {
			if(errno == EINTR)
				goto again;
			return -1;
		}
	}

	*ptr = 0;
	return n;
}

static int	read_cnt;
static char	*read_ptr;
static char	read_buf[MAXLINE];

static ssize_t
my_read(int fd, char *ptr)
{

	if (read_cnt <= 0) {
again:
		if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
			if (errno == EINTR)
				goto again;
			return(-1);
		} else if (read_cnt == 0)
			return(0);
		read_ptr = read_buf;
	}

	read_cnt--;
	*ptr = *read_ptr++;
	return(1);
}

ssize_t
readline2(int fd, void *vptr, size_t maxlen)
{
	ssize_t	n, rc;
	char	c, *ptr;

	ptr = (char *)vptr;
	for (n = 1; n < (ssize_t)maxlen; n++) {
		if ( (rc = my_read(fd, &c)) == 1) {
			*ptr++ = c;
			if (c == '\n')
				break;	/* newline is stored, like fgets() */
		} else if (rc == 0) {
			*ptr = 0;
			return(n - 1);	/* EOF, n - 1 bytes were read */
		} else
			return(-1);		/* error, errno set by read() */
	}

	*ptr = 0;	/* null terminate like fgets() */
	return(n);
}

ssize_t
readlinebuf(void **vptrptr)
{
	if (read_cnt)
		*vptrptr = read_ptr;
	return(read_cnt);
}
/* end readline */

int readable_timeout(int fd) {
	unsigned int usec;
	const int count = 1;
	fd_set rset;
	struct timeval tv;
	int i;
	int ret = -1;

	usec = (getenv(ENV_READ_TIMEOUT))?atoi(getenv(ENV_READ_TIMEOUT)):READ_TIMEOUT_DEFAULT;

	for(i = 0; i < count; i++) {
		tv.tv_sec = 0;
		tv.tv_usec = usec;
		FD_ZERO(&rset);
		FD_SET(fd,&rset);

		ret = select(fd+1,&rset,NULL,NULL,(usec?&tv:NULL));
		if(ret == 0)
			continue;
		else if(ret < 0) {
			MSG("select failed");
			return ret;
		}
	}

	return ret;
}

/*
 * @return -1 if error occur or time out
 * @return 0 EOF
 * @return nread
 */
int readline_timeout(int sockfd,void *vptr,size_t maxlen) {
	int ret;

	ret = readable_timeout(sockfd);
	if(ret == -1) {
		MSG("readable_timeout failed");
		return -1;
	}
	else if(ret == 0) {
		MSG("res_from_server timeout");
		return -1;
	}

	ret = readline(sockfd,vptr,maxlen);
	if(ret == -1) {
		MSG("readline failed");
		return -1;
	}

	return ret;
}


/*
static int	read_cnt;
static char	*read_ptr;
static char	read_buf[MAXLINE];

static ssize_t
my_read(int fd, char *ptr)
{

	if (read_cnt <= 0) {
again:
		if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
			if (errno == EINTR)
				goto again;
			return(-1);
		} else if (read_cnt == 0)
			return(0);
		read_ptr = read_buf;
	}

	read_cnt--;
	*ptr = *read_ptr++;
	return(1);
}

ssize_t
readline(int fd, void *vptr, size_t maxlen)
{
	ssize_t	n, rc;
	char	c, *ptr;

	ptr = (char *)vptr;
	for (n = 1; n < (ssize_t)maxlen; n++) {
		if ( (rc = my_read(fd, &c)) == 1) {
			*ptr++ = c;
			if (c == '\n')
				break;	// newline is stored, like fgets()
		} else if (rc == 0) {
			*ptr = 0;
			return(n - 1);	// EOF, n - 1 bytes were read 
		} else
			return(-1);		// error, errno set by read()
	}

	*ptr = 0;	// null terminate like fgets() 
	return(n);
}

ssize_t
readlinebuf(void **vptrptr)
{
	if (read_cnt)
		*vptrptr = read_ptr;
	return(read_cnt);
}
*/


int run_log(const char *fmt,...) {
	va_list ap;
	char buf[MAXLINE];
	time_t timeval;
	time(&timeval);
	snprintf(buf,MAXLINE,"%s",ctime(&timeval));
	if(buf[strlen(buf)-1] == '\n')
		buf[strlen(buf)-1] = '\t';

	va_start(ap,fmt);

	vsnprintf(buf+strlen(buf),MAXLINE-strlen(buf),fmt,ap);
	//puts(buf);
	printf("%s\n",buf);

	va_end(ap);
	
	return 0;
}

void sig_pipe(int signo) {
	run_log("got SIGPIPE");
}

int connect_timeout(int sockfd,struct sockaddr *saptr, socklen_t salen)
{
	int				flags, n, error;
	socklen_t		len;
	fd_set			rset, wset;
	struct timeval	tval;
	unsigned int microsec = 0;

	microsec = (getenv(ENV_CONNECT_TIMEOUT))?
			atoi(getenv(ENV_CONNECT_TIMEOUT)):CONNECT_TIMEOUT_DEFAULT;

	flags = fcntl(sockfd, F_GETFL, 0);
	fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);

	error = 0;
	if ( (n = connect(sockfd, saptr, salen)) < 0)
		if (errno != EINPROGRESS)
			return(-1);

	/* Do whatever we want while the connect is taking place. */

	if (n == 0)
		goto done;	/* connect completed immediately */

	FD_ZERO(&rset);
	FD_SET(sockfd, &rset);
	wset = rset;
	tval.tv_sec = 0;
	tval.tv_usec = microsec;

	if ( (n = select(sockfd+1, &rset, &wset, NULL,
					 microsec ? &tval : NULL)) == 0) {
		//close(sockfd);		/* timeout */
		errno = ETIMEDOUT;
		return(-1);
	}

	if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) {
		len = sizeof(error);
		if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
			return(-1);			/* Solaris pending error */
	} else
		return -1; //exit(1);//err_quit("select error: sockfd not set");

done:
	fcntl(sockfd, F_SETFL, flags);	/* restore file status flags */

	if (error) {
		//close(sockfd);		/* just in case */
		errno = error;
		return(-1);
	}
	return(0);
}
